package com.seednia.module.core.tagging.dto;

import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import org.codehaus.jackson.annotate.JsonIgnoreProperties;

import com.seednia.foundation.framework.dto.AbstractRecord;
import com.seednia.module.core.user.dto.UserDto;

/**
 * Records that can be tagged must extends this class.
 * 
 * @author Young Suk Ahn
 *
 */
@XmlRootElement(name = "TaggableAbstractRecord", 
		namespace = "http://seednia.com/foundation/1.0")
@XmlType(name = "TaggableAbstractRecord")
@XmlAccessorType(XmlAccessType.PROPERTY)
@JsonIgnoreProperties({ "taggingDtos" }) // For jackson
public abstract class AbstractTaggableRecord extends AbstractRecord implements ITaggable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 6653005277004039340L;

	// Tag types and it's respective TaggingDto's (this is mostly for Hibernate to use the correct table)
	private Map<String, Class<?> > tagTypeTaggingDtoClassMap = new HashMap<String, Class<?> >();
	private TaggingCollectionContainer taggingCollection = null;
	
	// provide default constructor even though this is abstract method.
	// The idea is this class should be a super class of all DTO and not be able
	// to be instantiated.
	public AbstractTaggableRecord(){
		super();
		this.taggingCollection = new TaggingCollectionContainer(this);
		// should not be called explicitly (implemented because com.sun.xml.bind.v2.runtime.IllegalAnnotationsException)
	}
	
	public AbstractTaggableRecord(String tagType, Class<?> taggingDtoClass)
	{
		this();
		addTagType(tagType, taggingDtoClass);
	}
	

	public final void addTagType(String tagType, Class<?> taggingDtoClass)
	{
		tagTypeTaggingDtoClassMap.put(tagType, taggingDtoClass);
		this.taggingCollection.addTagType(tagType);
	}

	// From ITaggable {{


	@Override
	public Collection<String> getTagTypes() {
		return tagTypeTaggingDtoClassMap.keySet();
	}

	@Override
	public Class<?> getTaggingDtoClass(String type) {
		return tagTypeTaggingDtoClassMap.get(type);
	}

	@Override
	public int size() {
		// TODO Auto-generated method stub
		return 0;
	}
	
	@Override
	public TaggingDto createTaggingDto(String type, ITaggable taggable,
			UserDto creatorDto, TagDto tagDto) {
		Class<?> dtoClass = this.getTaggingDtoClass(type);
		TaggingDto retval = null;
		try {
			Constructor<?> consturctor = dtoClass.getDeclaredConstructor(ITaggable.class);
			retval = (TaggingDto)consturctor.newInstance(taggable);
		} catch (Exception e) {
			throw new IllegalArgumentException(e);
		}  
		retval.setCreatorDto(creatorDto);
		retval.setTagDto(tagDto);
		return retval;
	}


	@Override
	public boolean containsTag(String type, String tagName) {
		return	taggingCollection.containsTag(type, tagName);
	}

	@Override
	public String[] getTagNames(String type) {
		return taggingCollection.getTagNames(type);
	}
	@Override
	public void setTagNames(String type, String[] tagNames) {
		taggingCollection.setTagNames(type, tagNames);
	}

	@Override
	public String getTagNamesCsv(String type)
	{
		return taggingCollection.getTagNamesCsv(type);
	}

	@Override
	public void setTagNamesCsv(String type, String tagNamesCsv)
	{
		if (tagNamesCsv == null) return;
		taggingCollection.setTagNamesCsv(type, tagNamesCsv);
	}
	
	/* I don't think we need this
	@Override
	public TagDto getTagDto(String type, String tagName)
	{
		return this.taggingCollection.getTagDto(type, tagName);
	}
	@Transient
	@Override
	public Collection<TagDto> getTagDtos(String type) {
		return taggingCollection.getTagDtos(type);
	}*/

	@Override
	public TaggingDto getTaggingDto(String type, String tagName) {
		return this.taggingCollection.getTaggingDto(type, tagName);
	}

	@Transient
	@Override
	public Collection<TaggingDto> getTaggingDtos(String type) {
		return taggingCollection.getTaggingDtos(type);
	}
	@Override
	public void setTaggingDtos(String type, Collection<TaggingDto> taggingDtos) {
		this.taggingCollection.setTaggingDtos(type, taggingDtos);
	}
	
	@Override
	public void addTaggingDto(String type, TaggingDto taggingDto) {
		taggingCollection.addTaggingDto(type, taggingDto);
	}

	@Override
	public void removeTaggingDto(String type, TaggingDto taggingDto) {
		taggingCollection.removeTaggingDto(type, taggingDto);
	}
	// }} From ITaggable
}
